#include "graphicsimageitem.h"
#include <sys/stat.h>
#include <QString>
#include <QPainter>
#include <cv.hpp>
#include <QDir>
#include <QDebug>
#include <highgui.h>
//#include "decklink/DeckLinkAPI.h"
template <typename T>
inline unsigned char clamp(T x) {
    return x>255 ? 255 : (x<0 ? 0:x);
}

void yuv2rgb(int Y, int Cr, int Cb, unsigned char* R, unsigned char* G, unsigned char* B,
             float r, float g, float b,
             float x, float y,
             float alpha, float beta, float gamma, float delta,
             float n)
{
    //todo
    *R = clamp(n/alpha*(Y - beta) + n*y/gamma*(Cr - delta));
    *G = clamp(n/alpha*(Y - beta) - n*b*x/(gamma*g)*(Cb - delta) - n*r*y/(gamma*g)*(Cr - delta));
    *B = clamp(n/alpha*(Y - beta) + n*x/gamma*(Cb - delta));
}

void ycrcb2rgb(int Y, int Cr, int Cb, unsigned char* R, unsigned char* G, unsigned char* B)
{
    //todo
    *R = clamp(1.164*(Y-16) + 1.596*(Cr-128));
    *G = clamp(1.164*(Y-16) - 0.813*(Cr-128)- 0.391*(Cb-128));
    *B = clamp(1.164*(Y-16) + 2.018*(Cb-128));

//    float r = 0.299;
//    float g = 0.587;
//    float b = 0.114;

//    float x = 2 * (1-b);
//    float y = 2 * (1-r);

//    float alpha = 219;
//    float beta = 16;
//    float gamma = 224;
//    float delta = 128;
//    float n = 255;

//    yuv2rgb(Y, Cr, Cb, R, G, B,
//            r, g, b, x, y, alpha, beta, gamma, delta, n);

//    qDebug() << "r:" << (int)r << " g:" << (int)g << " b:" << (int)b;
}

GraphicsImageItem::GraphicsImageItem(QString *rawFile, int width, int height, QGraphicsObject *parent) :
    QGraphicsObject(parent), BLOCKSIZE(width*height), m_ImageWidth(width), m_ImageHeight(height), STRBLOCKSIZE(256)
{
    m_Width = m_ImageWidth + EDGEWIDTH + EDGEWIDTH;
    m_Height = m_ImageHeight + EDGEHEIGHT + EDGEWIDTH;
    m_Filename = new QString(*rawFile);
    m_fp = fopen64(m_Filename->toLatin1().data(),"r");
    m_CurrentTimeStamp = new QString("");
//    IplImage *ipl = cvCreateImage(cvSize(m_ImageWidth,m_ImageHeight),IPL_DEPTH_8U,1);
    IplImage *ipl = cvCreateImage(cvSize(m_ImageWidth/2,m_ImageHeight),IPL_DEPTH_8U,3);
//    imageData = new IplQImage(ipl,1);
    imageData = new IplQImage(ipl, 3);
    cvReleaseImage(&ipl);

    struct stat64 buf;
    int ret = stat64(m_Filename->toLatin1().data(), &buf);
    m_FrameCount = buf.st_size / (BLOCKSIZE+STRBLOCKSIZE);

    readFrame(0);
}

GraphicsImageItem::~GraphicsImageItem()
{
    if(m_fp)
    {
        fclose(m_fp);
        m_fp = NULL;
    }
}

QRectF GraphicsImageItem::boundingRect() const
{
    //TODO
    return QRectF(0,0,m_Width,m_Height);
}

QPainterPath GraphicsImageItem::shape() const
{
    //TODO
    QPainterPath path;
    path.addRect(0,0,m_Width, m_Height);
    return path;
}

void GraphicsImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    //TODO
    Q_UNUSED(widget);
    if(imageData != NULL)
    {
        QImage *qImg = imageData->getQImage();
        painter->drawImage(EDGEWIDTH, EDGEHEIGHT, *qImg);
        painter->drawText(EDGEWIDTH,m_Height,*m_CurrentTimeStamp);
    }
}

off64_t GraphicsImageItem::FrameCount() const
{
    return m_FrameCount;
}

void GraphicsImageItem::onTimeStampUpdated(off64_t iframe)
{
    readFrame(iframe);
    this->update();
}

void GraphicsImageItem::readFrame(off64_t iframe)
{
    //TODO
    if(m_fp)
    {
        off64_t ret = fseeko64(m_fp, iframe*(BLOCKSIZE+STRBLOCKSIZE), SEEK_SET);
        buf = cvCreateImage(cvSize(m_ImageWidth,m_ImageHeight), IPL_DEPTH_8U, 1);
//        cvShowImage("test", buf);
        fread(buf->imageData, BLOCKSIZE, 1, m_fp);
        fseeko64(m_fp, iframe*(BLOCKSIZE+STRBLOCKSIZE)+BLOCKSIZE, SEEK_SET);
        char strbuf[STRBLOCKSIZE];
        fread(strbuf, STRBLOCKSIZE, 1, m_fp);
        if(m_CurrentTimeStamp)
        {
            m_CurrentTimeStamp->clear();
            m_CurrentTimeStamp->append(QString("%1").arg(strbuf));
        }

        // Convert YUV422 to RGB ---- Added by Lin
        IplImage *rgbBuf = cvCreateImage(cvSize(m_ImageWidth/2, m_ImageHeight), IPL_DEPTH_8U, 3);

        // Method 2
        for (int i=0, j=0; i<m_ImageWidth/2 * m_ImageHeight * 3; i+=6, j+=4){
            unsigned char cb = buf->imageData[j];
            unsigned char y1 = buf->imageData[j+1];
            unsigned char cr = buf->imageData[j+2];
            unsigned char y2 = buf->imageData[j+3];

            unsigned char r, g, b;
            ycrcb2rgb(y1, cb, cr, &r, &g, &b);
            rgbBuf->imageData[i] = b;
            rgbBuf->imageData[i+1] = g;
            rgbBuf->imageData[i+2] = r;

            ycrcb2rgb(y2, cb, cr, &r, &g, &b);
            rgbBuf->imageData[i+3] = b;
            rgbBuf->imageData[i+4] = g;
            rgbBuf->imageData[i+5] = r;
        }

        // Method 3
//        IDeckLinkVideoConversion convert;
        imageData->setImageChannels(3);

        // Code above is Added by Lin
        if(imageData)
        {
//            imageData->updateImage(buf);
            imageData->updateImage(rgbBuf);
//            imageData->updateImage(grayBuf);
        }
        cvReleaseImage(&buf);
        cvReleaseImage(&rgbBuf);
//        cvReleaseImage(&grayBuf);
    }


}

void GraphicsImageItem::saveFrames(QString path)
{
    //
    saveFrames(path, 0, m_FrameCount);
}

void GraphicsImageItem::saveFrames(QString path, int startFrame, int frameNo)
{
    //todo
    QImage *qImg = imageData->getQImage();
    int n1 = m_Filename->lastIndexOf("/");
    int n2 = m_Filename->lastIndexOf(".");
    QString dirName = m_Filename->left(n2).right(n2-n1);
    QString filepath;
    filepath.append(path).append(dirName);
    QDir newDir(filepath);
    if (!newDir.exists())
        newDir.mkdir(filepath);

    QString filename;
    int endFrame;
    if (startFrame + frameNo < m_FrameCount) endFrame = startFrame + frameNo;
    else endFrame = m_FrameCount;
    for (int i=startFrame; i<endFrame; i++) {
        readFrame(i);
        filename.clear();
        QString tsString;
        tsString = m_CurrentTimeStamp->replace(":","-");
        filename.append(filepath).append("/").append(tsString).append(".bmp");    // save bmp images
//        filename.append(filepath).append("/").append(tsString).append(".jpg");    // save jpg images
//        qDebug() << filename << endl;
        qImg->save(filename);
    }
}

void GraphicsImageItem::clamp(char* array)
{
    int max, min;
    max = min = 0;
    for (int i=0; i<m_ImageWidth/2 * m_ImageHeight * 3; i++)
    {
        max = (int) array[i] > max ? array[i] : max;
        min = (int) array[i] < min ? array[i] : min;
    }
    qDebug() << "max:" << max << " min:" << min;

    for (int i=0; i<m_ImageWidth/2 * m_ImageHeight * 3; i++)
    {
        array[i] -= min;
//        array[i] = (int) array[i] / (max-min) * 255.0;
//        qDebug() << array[i];
    }
}

void GraphicsImageItem::getFrameData(off64_t iframe, char *data)
{
    //todo
    if(m_fp)
    {
        data = (char*)malloc(sizeof(char)*BLOCKSIZE);
        off64_t ret = fseeko64(m_fp, iframe*(BLOCKSIZE+STRBLOCKSIZE), SEEK_SET);
        fread(data, BLOCKSIZE, 1, m_fp);
    }
}
